package com.yc.mypredicatefactory;

import org.lionsoul.ip2region.xdb.Searcher;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;

@Component
public class RegionRoutePredicateFactory extends AbstractRoutePredicateFactory<RegionRoutePredicateFactory.Config> {

    private static final String COUNTRY_KEY = "country";
    private static final String REGION_HEADER = "X-Forwarded-For";
    private final Searcher searcher;

    public RegionRoutePredicateFactory() {
        super(Config.class);
        try {
            // 加载IP2Region数据库（需提前下载ip2region.xdb文件）
            Resource resource = new ClassPathResource("ip2region.xdb");
            this.searcher = Searcher.newWithFileOnly(resource.getFile().getPath());
        } catch (IOException e) {
            throw new RuntimeException("Failed to initialize IP2Region searcher", e);
        }
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList(COUNTRY_KEY);
    }

    @Override
    public GatewayPredicate apply(Config config) {
        return new GatewayPredicate() {
            public Mono<Boolean> apply(ServerWebExchange exchange) throws UnknownHostException {

                ServerHttpRequest request = exchange.getRequest();
                // 获取客户端IP（支持代理场景）

                InetAddress localhost = InetAddress.getLocalHost();
                // 获取IP地址的字符串形式
                String ip = localhost.getHostAddress();
                System.out.println(ip);
                if (ip == null) {
                    return Mono.just(false);
                }

                // 查询IP对应的国家
                String country = queryCountry(ip);
                if (country == null) {
                    return Mono.just(false);
                }

                // 判断是否匹配配置的国家列表
                return Mono.just(config.getCountries().contains(country));
            }

            @Override
            public String toString() {
                return String.format("Region: countries=%s", config.getCountries());
            }

            // 新增：实现Predicate接口的test方法
            @Override
            public boolean test(ServerWebExchange exchange) {
                try {
                    return apply(exchange).block();
                } catch (UnknownHostException e) {
                    throw new RuntimeException(e);
                }
            }
        };
    }

    private String extractClientIp(ServerHttpRequest request) {
        String xffHeader = request.getHeaders().getFirst(REGION_HEADER);
        if (xffHeader != null) {
            // 处理多级代理（格式：client,proxy1,proxy2）
            String[] ips = xffHeader.split(",");
            if (ips.length > 0) {
                return ips[0].trim();
            }
        }
        // 直接连接时获取远程地址
        return request.getRemoteAddress() != null ?
                request.getRemoteAddress().getAddress().getHostAddress() : null;
    }

    private String queryCountry(String ip) {
        try {
            // 使用IP2Region查询国家信息
            String region = searcher.search(ip);
            if (region != null) {
                // 解析格式: 国家|区域|省份|城市|运营商
                String[] parts = region.split("\\|");
                System.out.println(region);
                if (parts.length >= 1 && !"0".equals(parts[0])) {
                    return parts[0];
                }
            }
            return null;
        } catch (Exception e) {
            // 记录异常但不中断请求流程
            System.err.println("Failed to query IP region: " + ip + ", error: " + e.getMessage());
            return null;
        }
    }

    public static class Config {
        private List<String> countries;

        public List<String> getCountries() {
            return countries;
        }

        public Config setCountries(List<String> countries) {
            this.countries = countries;
            return this;
        }

        public Config setCountry(String country) {
            this.countries = Collections.singletonList(country);
            return this;
        }

        public Config setCountries(String... countries) {
            Assert.notEmpty(countries, "At least one country must be specified");
            this.countries = Arrays.asList(countries);
            return this;
        }
    }
}